package com.sysware.p2m.service;

import com.sysware.p2m.entity.DataObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Transactional
@Service(DataServiceImpl.BEAN_NAME)
public class DataServiceImpl implements DataService {
    public static final String BEAN_NAME = "dataServiceImpl";

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    @Override
    public List<DataObject> getDataObjectList(String userId, int pageNum, int pageSize) {
        String sql = "SELECT\n" +
                "    *\n" +
                "FROM\n" +
                "    PM_TEST_DATA_OBJECT\n" +
                "WHERE\n" +
                "    creatorid = :userId\n" +
                "ORDER BY\n" +
                "    createtime DESC";
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("userId", userId);
//        List<Map<String, Object>> list = namedParameterJdbcTemplate.queryForList(getPageSql(sql, pageNum, pageSize), paramMap);
//        System.out.println(list);
        return namedParameterJdbcTemplate.query(getPageSql(sql, pageNum, pageSize), new MapSqlParameterSource(paramMap), new BeanPropertyRowMapper<>(DataObject.class));
    }

    @Override
    public Long getDataObjectListCount(String userId) {
        String sql = "SELECT\n" +
                "    count(*) \n" +
                "FROM\n" +
                "    PM_TEST_DATA_OBJECT\n" +
                "WHERE\n" +
                "    creatorid = :userId\n";
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("userId", userId);
        return namedParameterJdbcTemplate.queryForObject(sql, paramMap, Long.class);
    }

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Override
    public void saveDataObject(List<DataObject> dataObjects) {
        String[] keys = "id,name,creatorId,createTime,statusId".split(",");
        Map<String, Object>[] batchValues = new HashMap[dataObjects.size()];
        for (int i = 0; i < dataObjects.size(); i++) {
            HashMap<String, Object> map = new HashMap<>(keys.length);
            BeanWrapperImpl beanWrapper = new BeanWrapperImpl(dataObjects.get(i));
            for (String key : keys) {
                map.put(key, beanWrapper.getPropertyValue(key));
            }
            batchValues[i] = map;
        }

        String sql = "INSERT INTO pm_test_data_object(id,name,creatorId,createTime,statusId) VALUES (:id, :name, :creatorId, :createTime, :statusId)";
        namedParameterJdbcTemplate.batchUpdate(sql, batchValues);
    }

    @Override
    public void updateDataObject(String[] ids, String statusId) {
        String sql = "update pm_test_data_object set statusId=:statusId where id in (:ids)";
        List<String> idList = Arrays.asList(ids);
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("ids", idList);
        paramMap.put("statusId", statusId);
        namedParameterJdbcTemplate.update(sql, paramMap);
    }

    @Autowired
    TransactionTemplate transactionTemplate;

    @Override
    public void excuteTransactionTemplate(String idA, String idB, int num) {
        transactionTemplate.execute(new TransactionCallback<Boolean>() {
            @Override
            public Boolean doInTransaction(TransactionStatus status) {
                doSomething(idA, idB, num);
                return true;
            }
        });
    }

    @Autowired
    PlatformTransactionManager transactionManager;

    @Override
    public void executeTransactionManager(String idA, String idB, int num) {
        DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
        TransactionStatus transactionStatus = transactionManager.getTransaction(definition);
        try {
            doSomething(idA, idB, num);
            transactionManager.commit(transactionStatus);
        } catch (Exception e) {
            transactionManager.rollback(transactionStatus);
            e.printStackTrace();
        }
    }

    @Override
    public void doSomething(String idA, String idB, int num) {
        if (num <= 0) {
            throw new RuntimeException("不足0元，不需操作");
        }

        String sql1 = "SELECT\n" +
                "     NVL(account, 0)\n" +
                " FROM\n" +
                "     pm_test_data_object\n" +
                " WHERE\n" +
                "     id = ?";
        BigDecimal accountA = jdbcTemplate.queryForObject(sql1, BigDecimal.class, idA);
        BigDecimal numAccout = new BigDecimal(num);
        if (accountA.compareTo(numAccout) < 0) {
            logger.error("余额不足 idA={}, num={}", idA, num);
            throw new RuntimeException("余额不足");
        }

        BigDecimal accountB = jdbcTemplate.queryForObject(sql1, BigDecimal.class, idB);
        logger.info("begin transfer  idA={}, accountA={}  to idB={}, accountB={}  num={}",
                idA, accountA.toString(), idB, accountB.toString(), num);

        String sql2 = "UPDATE pm_test_data_object\n" +
                "SET\n" +
                "    account = ?\n" +
                "WHERE\n" +
                "    id = ?";
        jdbcTemplate.update(sql2, accountA.subtract(numAccout), idA);
        jdbcTemplate.update(sql2, accountB.add(numAccout), idB);

        BigDecimal accountANew = jdbcTemplate.queryForObject(sql1, BigDecimal.class, idA);
        BigDecimal accountBNew = jdbcTemplate.queryForObject(sql1, BigDecimal.class, idB);
        logger.info("after transfer  idA={}, accountA={}  to idB={}, accountB={}  num={}",
                idA, accountANew.toString(), idB, accountBNew.toString(), num);

    }

    private String getPageSql(String sql, int pageNum, int pageSize) {
        int start = (pageNum - 1) * pageSize;
        int limit = pageNum * pageSize;
        StringBuilder pageSql = new StringBuilder();
        pageSql.append("select * from (\n" +
                "SELECT\n" +
                "    ROWNUM rownum_,\n" +
                "    row_.*\n" +
                "FROM\n" +
                "    (\n")
                .append(sql)
                .append("    ) row_\n" +
                        "    where rownum <= ")
                .append(limit)
                .append(" ) where rownum_ > ")
                .append(start)
        ;
        return pageSql.toString();
    }

}
